Server Functionsをclientから呼んでるのはどうやってんの?
このようなserver componentsの中で
code:app/posts/page.tsx
export default async function Page() {
return (
<div>
<Client />
</div>
);
}
以下のclient componentsを使用してる
code:src/Client.tsx
"use client";
import { useTransition } from "react";
import { increment } from "../app/posts/actions";
export default function Client() {
const startTransition = useTransition();
return (
<button onClick={() => startTransition(increment)}>Add To Cart</button>
);
}
server上で定義された関数をclientから呼んでいるわけだが、どうやってんの?
devtoolsでHTMLを見てみると
https://gyazo.com/84d954d24006deaa689b484f8e4b99b6
event handlerがいないので特に情報は得られなかった
別の場所から見てるのだろう
devtoolsのsourceパネルを見る
_next/static/chunks/app/posts/page.jsの中にincrementという関数を定義しているのを見つけた
code:js
var increment = (0,
private_next_rsc_action_client_wrapper__WEBPACK_IMPORTED_MODULE_2__"default")("a9992fb50d5b4db38c8b3fc103399157fb383700"); ;// Wrapped in an IIFE to avoid polluting the global scope
;(function() {
var _a, _b;
...
という感じでゴニョゴニョ書いている
内部の1行目が重要で、それ以外の部分はrefreshとかの処理が書いてるだけ
1行目でa9992fb50d5b4db38c8b3fc103399157fb383700というhashを引数に関数を呼んでる
その関数の定義はすぐ上に書かれてあって
code:js
var private_next_rsc_action_client_wrapper__WEBPACK_IMPORTED_MODULE_2__ =
__webpack_require__(
/*! private-next-rsc-action-client-wrapper */
"(app-client)/./node_modules/next/dist/build/webpack/loaders/next-flight-loader/action-client-wrapper.js"
);
なんかのmoduleを呼んでるだけ
つまり、このpathの内部にdefaultという名前の関数が定義されているはず
上記のpathの中身を見に行くと
code:./node_modules/next/dist/build/webpack/loaders/next-flight-loader/action-client-wrapper.js
// This file must be bundled in the app's client layer, it shouldn't be directly
// imported by the server.
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, // A noop wrapper to let the Flight client create the server reference.
"default", {
enumerable: true,
get: function() {
return _default;
}
});
const _appcallserver = require("next/dist/client/app-call-server");
function _default(id) {
// Since we're using the Edge build of Flight client for SSR 1, here we need to // also use the same Edge build to create the reference. For the client bundle,
// we use the default and let Webpack to resolve it to the correct version.
const { createServerReference } = typeof window === "undefined" ? require("react-server-dom-webpack/client.edge") : require("react-server-dom-webpack/client");
return createServerReference(id, _appcallserver.callServer);
}
//# sourceMappingURL=action-client-wrapper.js.map
引数にidを取るdefault()がexportされている
見づらいのでtsのコードの方を見る
createServerReference()という関数を呼んでいる
この実装はclient/serverのどちらで呼ぶかで内容がかわるみたい
要は、callServer(id)がやりたいだけ ref なので、callServerの方を見に行けばよいか
callServer
getServerActionDispatcherを呼んでるだけ
getServerActionDispatcher
globalServerActionDispatcherを呼んでる
再代入可能に定義された関数
useServerActionDispatcherというhooksの中でglobalServerActionDispatcherの中身を入れ替えてる
reducerとして定義されたACTION_SERVER_ACTIONをdispatch
router-reducer.tsの中を見ると
ACTION_SERVER_ACTIONというactionに対し、serverActionReducerという関数を呼んでる
serverActionReducer
気になっていたコードに到達したmrsekut.icon
内部でfetchServerAction()という関数を呼んで、server actionを作成している
fetchServerAction()の定義も同じファイル内にされている
fetchServerAction()
code:ts
...
const body = await encodeReply(actionArgs)
const res = await fetch('', {
method: 'POST',
headers: {
Accept: RSC_CONTENT_TYPE_HEADER,
'Next-Action': actionId,
...(process.env.__NEXT_ACTIONS_DEPLOYMENT_ID &&
process.env.NEXT_DEPLOYMENT_ID
? {
'x-deployment-id': process.env.NEXT_DEPLOYMENT_ID,
}
: {}),
...(state.nextUrl
? {
}
: {}),
},
body,
})
...
内部でfetch()を使って、POSTしてる!
Next-Action headerにactionIdを指定してることなどがわかる
dev toolのnetworkを見ると、クリックした時に以下のようなrequestを送ってるのがわかる
https://gyazo.com/5aa68bd42fba3a521ef4367d73da786f
このNext-Action headerのvalueが上記のhashと一致している
同じくコードを深ぼっていってる記事
この記事ではformのactionに与えた時の方を見てる